package com.yjt.common.security;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;

/**
 * 利用AES算法进行加密、解密工具类
 * 
 * @author hqy 2013-8-23
 * @version 01.00
 */
public class AESCipher {
	private final static String DEFAUL_STRING_ENCODING = "utf-8";

	/**
	 * 密钥算法
	 */
	private final static String KEY_ALGORITHM = "AES";

	/**
	 * <p>
	 * Cipher转换格式：算法/模式/填充、算法
	 * </p>
	 * <p>
	 * 可选值：
	 * </p>
	 * <p>
	 * 算法:AES、AESWrap
	 * </p>
	 * <p>
	 * 模式:NONE、CBC、CFB、CFBx、CTR、CTS、ECB、OFB、OFBx、PCBC
	 * </p>
	 * <p>
	 * 填充:NoPadding、ISO10126Padding、OAEPPadding、OAEPWith<digest>And<mgf>Padding、
	 * PKCS1Padding、PKCS5Padding、SSL3Padding
	 * </p>
	 */
	private final static String CIPHER_ALGORITHNM = "AES/CBC/PKCS5Padding";
	/**
	 * CBC模式，需要一个初始化向量iv，随机值
	 */
	private final static IvParameterSpec CBC_IV = new IvParameterSpec(
			"8241958433549347".getBytes());

	private AESCipher() {
	}

	/**
	 * 使用AES算法加密数据
	 * 
	 * @param sourceData
	 *            需要加密的原始数据
	 * @param key
	 *            用来加密的对称密钥(Base64编码格式)，可为任意长度
	 * @return 加密后的密文，以Base64编码格式返回
	 * @throws Exception
	 */
	public static String encrypt(String sourceData, String key)
			throws Exception {
		byte[] sourceBytes = sourceData.getBytes(DEFAUL_STRING_ENCODING);

		// 构造AES算法使用的对称密钥
		byte[] realKeyBytes = genarateKey(key);

		// 加密
		byte[] encryptData = encrypt(sourceBytes, realKeyBytes);

		// 加密后的数据进行Base64编码
		byte[] encodeBytes = Base64.encodeBase64(encryptData);
		String encryptString = new String(encodeBytes, DEFAUL_STRING_ENCODING);

		return encryptString;
	}

	/**
	 * 使用AES算法加密数据
	 * 
	 * @param sourceData
	 *            需要加密的原始数据
	 * @param key
	 *            用来加密的对称密钥，AES支持的密钥长度： 128, 192, and 256 bits
	 * @return 加密后的密文
	 * @throws Exception
	 */
	private static byte[] encrypt(byte[] sourceData, byte[] key)
			throws Exception {
		SecretKeySpec secretKey = new SecretKeySpec(key, KEY_ALGORITHM);

		// 获取实现指定转换的Cipher对象，此处AES为128位（Java默认为128位）
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHNM);
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, CBC_IV);// 使用CBC模式，需要一个向量iv
		byte[] encryptData = cipher.doFinal(sourceData);

		return encryptData;
	}

	/**
	 * 使用AES算法解密数据
	 * 
	 * @param encryptData
	 *            需要解密的数据(Base64编码格式)
	 * @param key
	 *            用来解密的对称密钥(Base64编码格式)，可为任意长度
	 * @return 解密后的原始数据
	 * @throws Exception
	 */
	public static String decrypt(String encryptData, String key)
			throws Exception {
		byte[] encryptBytes = encryptData.getBytes(DEFAUL_STRING_ENCODING);
		byte[] realEncryptBytes = Base64.decodeBase64(encryptBytes);

		// 构造AES算法使用的对称密钥
		byte[] realKeyBytes = genarateKey(key);

		// 解密
		byte[] decryptData = decrypt(realEncryptBytes, realKeyBytes);

		String decryptString = new String(decryptData, DEFAUL_STRING_ENCODING);

		return decryptString;
	}

	/**
	 * 使用AES算法解密数据
	 * 
	 * @param encryptData
	 *            需要解密的数据
	 * @param key
	 *            用来解密的对称密钥，AES支持的密钥长度： 128, 192, and 256 bits
	 * @return 解密后的原始数据
	 * @throws Exception
	 */
	private static byte[] decrypt(byte[] encryptData, byte[] key)
			throws Exception {
		SecretKeySpec secretKey = new SecretKeySpec(key, KEY_ALGORITHM);

		// 获取实现指定转换的Cipher对象，此处AES为128位（Java默认为128位）
		Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHNM);
		cipher.init(Cipher.DECRYPT_MODE, secretKey, CBC_IV);// 使用CBC模式，需要一个向量iv
		byte[] decryptData = cipher.doFinal(encryptData);

		return decryptData;
	}

	/**
	 * 根据给定的随机源生成AES算法支持的128位的密钥
	 * 
	 * @param sead
	 * @return
	 * @throws Exception
	 */
	private static byte[] genarateKey(String sead) throws Exception {
		/* 从缓存中获取对称密钥 */
		byte[] enCodeFormat = AESKeyManager.getAESKeyBySead(sead);
		if (null != enCodeFormat) {
			return enCodeFormat;
		}
		
		/* 缓存中没有相应的对称密钥，需重新生成 */
		// 对随机源进行Base64编码
		byte[] seadBytes = sead.getBytes(DEFAUL_STRING_ENCODING);
		byte[] decodeSeadBytes = Base64.decodeBase64(seadBytes);
		
		KeyGenerator kgen = KeyGenerator.getInstance(KEY_ALGORITHM);
		SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
		secureRandom.setSeed(decodeSeadBytes);
		
		// 根据随机源生成128位的密钥，AES支持的密钥长度： 128, 192, and 256 bits
		kgen.init(128, secureRandom);
		SecretKey secretKey = kgen.generateKey();
		enCodeFormat = secretKey.getEncoded();
		
		// 保存对称密钥到缓存中
		AESKeyManager.setAESKey(sead, enCodeFormat);

		return enCodeFormat;
	}
}
